package com.gz.moreaseManager.utils;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Pattern;

/**
 * 基本工具类
 */
public final class UtilTool {

	private static final Log logger = LogFactory.getLog(UtilTool.class);
	private static final String CONTENT_TYPE_HTML = "text/html;charset=UTF-8";
	private static final String MD5_SALT = "BY_SPRING_BOOT_DEMO";
	private static boolean isThreadLocalRandomAvailable = false;
	private static ReentrantLock lock = new ReentrantLock();
	private static long leastSigBits, lastTime;
	private static Random random;
	/**
	 * 正则表达式：验证手机号
	 */
	private static final String REGEX_MOBILE = "^[1](([3][0-9])|([4][5,7,9])|([5][^4,6,9])|([6][6])|([7][3,5,6,7,8])|([8][0-9])|([9][8,9]))[0-9]{8}$";
	/**
	 * 正则表达式：验证邮箱
	 */
	private static final String REGEX_EMAIL = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$";
	/**
	 * 正则表达式：验证身份证
	 */
	private static final String REGEX_ID_CARD = "(^\\d{18}$)|(^\\d{15}$)";
	/**
	 * 正则表达式：验证IP地址
	 */
	private static final String REGEX_IP_ADDR = "(25[0-5]|2[0-4]\\d|[0-1]\\d{2}|[1-9]?\\d)";
	/**
	 * 正则表达式：验证URL
	 */
	private static final String REGEX_URL = "http(s)?://([\\w-]+\\.)+[\\w-]+(/[\\w- ./?%&=]*)?";
	/**
	 * 正则表达式：验证汉字
	 */
	private static final String REGEX_CHINESE = "^[\u4e00-\u9fa5],{0,}$";

	static {
		try {
			isThreadLocalRandomAvailable = null != UtilTool.class.getClassLoader().loadClass("java.util.concurrent.ThreadLocalRandom");
		} catch (ClassNotFoundException e) {
			logger.error(e);
		}
		byte[] seed = new SecureRandom().generateSeed(8);
		leastSigBits = new BigInteger(seed).longValue();
		if (!isThreadLocalRandomAvailable) {
			random = new SecureRandom(seed);
		}
	}

	private UtilTool() {
		/**
		 * 构造函数，禁止实例化
		 */
	}

	/**
	 * 空对象判断
	 *
	 * @param obj 判断对象
	 * @return 判断结果
	 */
	public static boolean isBlank(Object obj) {
		return obj == null || "".equals(obj) || "null".equals(obj);
	}

	/**
	 * 空对象判断
	 *
	 * @param obj 判断对象
	 * @return 判断结果
	 */
	public static boolean isNotBlank(Object obj) {
		return !isBlank(obj);
	}

	/**
	 * 处理空字符串
	 *
	 * @param str 字符串
	 * @return 处理后字符串
	 */
	public static String replaceNull(String str) {
		if (str == null) {
			return "";
		} else if ("null".equals(str)) {
			return "";
		} else {
			return str;
		}
	}

	/**
	 * 字节转换为浮点
	 *
	 * @param b     字节（至少4个字节）
	 * @param index 开始位置
	 * @return 浮点数据
	 */
	public static float byte2Float(byte[] b, int index) {
		int l;
		l = b[index];
		l &= 0xff;
		l |= ((long) b[index + 1] << 8);
		l &= 0xffff;
		l |= ((long) b[index + 2] << 16);
		l &= 0xffffff;
		l |= ((long) b[index + 3] << 24);
		return Float.intBitsToFloat(l);
	}

	/**
	 * 字符数组转Double
	 *
	 * @param arr byte数组
	 * @return 双精度浮点数据
	 */
	public static double bytes2Double(byte[] arr) {
		long value = 0;
		for (int i = 0; i < 8; i++) {
			value |= ((long) (arr[i] & 0xff)) << (8 * i);
		}
		return Double.longBitsToDouble(value);
	}

	/**
	 * long转byte数组
	 *
	 * @param num long数据
	 * @return byte数组
	 */
	public static byte[] long2Bytes(long num) {
		byte[] byteNum = new byte[8];
		for (int ix = 0; ix < 8; ++ix) {
			int offset = 64 - (ix + 1) * 8;
			byteNum[ix] = (byte) ((num >> offset) & 0xff);
		}
		return byteNum;
	}

	/**
	 * 字符转byte
	 *
	 * @param c char数据
	 * @return byte数据
	 */
	public static byte char2Byte(char c) {
		return (byte) "0123456789ABCDEF".indexOf(c);
	}

	/**
	 * byte数组转十六进制字符串
	 *
	 * @param bytes byte数组
	 * @return 十六进制字符串
	 */
	public static String byte2HexStr(byte[] bytes) {
		StringBuilder hexStr = new StringBuilder();
		String step;
		for (byte b : bytes) {
			step = (Integer.toHexString(b & 0XFF));
			if (step.length() == 1) {
				hexStr.append("0").append(step);
			} else {
				hexStr.append(step);
			}
		}
		return hexStr.toString().toLowerCase(Locale.getDefault());
	}

	/**
	 * 生成指定长度的随机数字串
	 *
	 * @param length 生成数字长度
	 * @return 随机数字字符串
	 */
	public static String random(int length) {
		int max = 9;
		StringBuilder sb = new StringBuilder();
		Random random = new SecureRandom();
		for (int i = 0; i < length; i++) {
			int s = random.nextInt(max);
			sb.append(s);
		}
		return sb.toString();
	}

	/**
	 * 获取文件后缀
	 *
	 * @param fileName 文件名
	 * @return 文件后缀(不包括.)
	 */
	public static String getSuffix(String fileName) {
		if (isBlank(fileName) || fileName.indexOf('.') < 0)
			return null;
		return fileName.substring(fileName.lastIndexOf('.') + 1);
	}

	/**
	 * 生成新文件名
	 *
	 * @param fileName 旧文件名
	 * @return 新文件名
	 */
	public static String getNewFileName(String fileName) {
		if (!isBlank(fileName)) {
			int dot = fileName.lastIndexOf('.');
			if ((dot > -1) && (dot < (fileName.length() - 1))) {
				String timeStr = DateUtil.dateToStr(new Date(), "yyyyMMddHHmmss");
				return timeStr + random(3) + fileName.substring(dot);
			}
		}
		return fileName;
	}

	/**
	 * 获取IP地址
	 *
	 * @param request 请求对象
	 * @return IP地址
	 */
	public static String getRemoteHost(HttpServletRequest request) {
		String ip = request.getHeader("x-forwarded-for");
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddr();
		}
		return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
	}

	/**
	 * 校验手机号
	 *
	 * @param regStr 校验字符串
	 * @return 检验结果
	 */
	public static boolean isMobile(String regStr) {
		return Pattern.matches(REGEX_MOBILE, regStr);
	}

	/**
	 * 校验邮箱
	 *
	 * @param regStr 校验字符串
	 * @return 检验结果
	 */
	public static boolean isEmail(String regStr) {
		return Pattern.matches(REGEX_EMAIL, regStr);
	}

	/**
	 * 校验身份证
	 *
	 * @param regStr 校验字符串
	 * @return 检验结果
	 */
	public static boolean isIDCard(String regStr) {
		return Pattern.matches(REGEX_ID_CARD, regStr);
	}

	/**
	 * 校验IP地址
	 *
	 * @param regStr 校验字符串
	 * @return 检验结果
	 */
	public static boolean isIPAddr(String regStr) {
		return Pattern.matches(REGEX_IP_ADDR, regStr);
	}

	/**
	 * 校验URL
	 *
	 * @param regStr 校验字符串
	 * @return 检验结果
	 */
	public static boolean isUrl(String regStr) {
		return Pattern.matches(REGEX_URL, regStr);
	}

	/**
	 * 校验汉字
	 *
	 * @param regStr 校验字符串
	 * @return 检验结果
	 */
	public static boolean isChinese(String regStr) {
		return Pattern.matches(REGEX_CHINESE, regStr);
	}

	/**
	 * 随机数生成UUID
	 *
	 * @return UUID字符串
	 */
	public static String getRandUUID() {
		byte[] randomBytes = new byte[16];
		if (isThreadLocalRandomAvailable)
			java.util.concurrent.ThreadLocalRandom.current().nextBytes(randomBytes);
		else
			random.nextBytes(randomBytes);
		long mostSigBits = 0;
		for (int i = 0; i < 8; i++)
			mostSigBits = (mostSigBits << 8) | (randomBytes[i] & 0xff);
		long leastSigBits = 0;
		for (int i = 8; i < 16; i++)
			leastSigBits = (leastSigBits << 8) | (randomBytes[i] & 0xff);
		return new UUID(mostSigBits, leastSigBits).toString().replaceAll("-", "");
	}

	/**
	 * 时间戳生成UUID
	 *
	 * @return 时间戳字符串
	 */
	public static String getDateUUID() {
		long timeMillis = (System.currentTimeMillis() * 10_000) + 0x01B2_1DD2_1381_4000L;
		lock.lock();
		try {
			if (timeMillis > lastTime)
				lastTime = timeMillis;
			else
				timeMillis = ++lastTime;
		} finally {
			lock.unlock();
		}
		long mostSigBits = timeMillis << 32;
		mostSigBits |= (timeMillis & 0xFFFF_0000_0000L) >> 16;
		mostSigBits |= 0x1000 | ((timeMillis >> 48) & 0x0FFF);
		return new UUID(mostSigBits, leastSigBits).toString().replaceAll("-", "");
	}

	/**
	 * 检查数组中的元素是否都在另一个数组内
	 *
	 * @param bytes1 待检查数组
	 * @param bytes2 数组
	 * @return 结果
	 */
	public static boolean checkValueContain(Byte[] bytes1, Byte[] bytes2) {
		for (Byte status : bytes1) {
			boolean checkActiveStatusParam = false;
			for (Byte activeStatu : bytes2) {
				if (activeStatu.equals(status)) {
					checkActiveStatusParam = true;
					break;
				}
			}
			if (!checkActiveStatusParam) return false;
		}
		return true;
	}

	/**
	 * 字符串数组用英文逗号连接
	 *
	 * @param arrays 字符串数组
	 * @return 连接字符串
	 */
	public static String arrayJoinToString(String[] arrays) {
		if (arrays == null || arrays.length == 0) return null;
		return Arrays.stream(arrays).reduce((a, b) -> a + "," + b).get();
	}

	/**
	 * 高效对象属性复制
	 *
	 * @param data  源对象
	 * @param clazz 目标类
	 */
	public static <T> T copyBean(Object data, Class<T> clazz) throws ReflectiveOperationException {
		if (data == null || clazz == null) return null;
		Class<?> sourceClass = data.getClass(), targetClass = clazz;
		List<Field> sourceFields = new ArrayList<>(), targetFields = new ArrayList<>();
		for (; sourceClass != Object.class; sourceClass = sourceClass.getSuperclass()) { // 向上循环,遍历父类
			sourceFields.addAll(Arrays.asList(sourceClass.getDeclaredFields()));
		}
		for (; targetClass != Object.class; targetClass = targetClass.getSuperclass()) {
			targetFields.addAll(Arrays.asList(targetClass.getDeclaredFields()));
		}
		T result = clazz.newInstance();
		for (Field source : sourceFields) {
			source.setAccessible(true);
			Object value = source.get(data);
			if (isBlank(value)) continue;
			for (Field target : targetFields) {
				target.setAccessible(true);
				if (source.getName().equals(target.getName()) && source.getType().equals(target.getType()))
					target.set(result, value);
			}
		}
		return result;
	}

	/**
	 * List分割
	 *
	 * @param list 源数据
	 * @param szie 分割大小
	 * @param <T>  数据泛型
	 * @return List分割后集合
	 */
	public static <T> List<List<T>> averageAssign(List<T> list, int szie) {
		int reder = list.size() % szie, num = list.size() / szie, offset = 0;
		List<List<T>> result = new ArrayList<>();
		List<T> value;
		for (int i = 0; i < szie; i++) {
			if (reder > 0) {
				value = list.subList(i * num + offset, (i + 1) * num + offset + 1);
				reder--;
				offset++;
			} else
				value = list.subList(i * num + offset, (i + 1) * num + offset);
			result.add(value);
		}
		return result;
	}
}